#include "rtda/heap/class.h"
#include "rtda/heap/constant_pool.h"
#include "rtda/heap/field.h"
#include "rtda/heap/method.h"
#include "rtda/heap/object.h"
#include <algorithm>

namespace jvm {
namespace heap {

Class::Class(std::shared_ptr<classfile::ClassFile> pClassFile) {
    m_accessFlags = pClassFile->getAccessFlags();
    m_name = pClassFile->getClassName();
    m_superClassName = pClassFile->getSuperClassName();
    m_interfaceNames = pClassFile->getInterfaceNames();
    m_pConstantPool = newConstantPool(this, pClassFile->getConstantPool());
    m_fields = newFields(this, pClassFile->getFields());
    m_methods = newMethods(this, pClassFile->getMethods());
}

bool Class::isPublic() const { return 0 != (m_accessFlags & ACC_PUBLIC); }
bool Class::isFinal() const { return 0 != (m_accessFlags & ACC_FINAL); }
bool Class::isSuper() const { return 0 != (m_accessFlags & ACC_SUPER); }
bool Class::isInterface() const { return 0 != (m_accessFlags & ACC_INTERFACE); }
bool Class::isAbstract() const { return 0 != (m_accessFlags & ACC_ABSTRACT); }
bool Class::isSynthetic() const { return 0 != (m_accessFlags & ACC_SYNTHETIC); }
bool Class::isAnnotation() const { return 0 != (m_accessFlags & ACC_ANNOTATION); }
bool Class::isEnum() const { return 0 != (m_accessFlags & ACC_ENUM); }

std::shared_ptr<ConstantPool> Class::getConstantPool() const { return m_pConstantPool; }
std::shared_ptr<Slots> Class::getStaticVars() const { return m_pStaticVars; }

bool Class::isAccessibleTo(std::shared_ptr<Class> pOther) const {
    return isPublic() || getPackageName() == pOther->getPackageName();
}

std::string Class::getPackageName() const {
    size_t pos = m_name.find_last_of('/');
    if (pos != std::string::npos) {
        return m_name.substr(0, pos);
    }
    return "";
}

std::shared_ptr<Method> Class::getMainMethod() const {
    return getStaticMethod("main", "([Ljava/lang/String;)V");
}

std::shared_ptr<Method> Class::getStaticMethod(const std::string& name, const std::string& descriptor) const {
    for (auto& pMethod : m_methods) {
        if (pMethod->isStatic() && pMethod->getName() == name && pMethod->getDescriptor() == descriptor) {
            return pMethod;
        }
    }
    return nullptr;
}

std::shared_ptr<Object> Class::newObject() const {
    return newObject(this);
}

bool Class::isAssignableFrom(std::shared_ptr<Class> pOther) const {
    if (this == pOther) {
        return true;
    }

    if (!isInterface()) {
        return pOther->isSubClassOf(this);
    } else {
        return pOther->isImplements(this);
    }
}

bool Class::isSubClassOf(std::shared_ptr<Class> pOther) const {
    for (std::shared_ptr<Class> pSuper = m_pSuperClass; pSuper != nullptr; pSuper = pSuper->m_pSuperClass) {
        if (pSuper == pOther) {
            return true;
        }
    }
    return false;
}

bool Class::isImplements(std::shared_ptr<Class> pIface) const {
    for (std::shared_ptr<Class> pClass = const_cast<std::shared_ptr<Class>>(this); pClass != nullptr; pClass = pClass->m_pSuperClass) {
        for (std::shared_ptr<Class> pInterface : pClass->m_interfaces) {
            if (pInterface == pIface || pInterface->isSubInterfaceOf(pIface)) {
                return true;
            }
        }
    }
    return false;
}

bool Class::isSubInterfaceOf(std::shared_ptr<Class> pIface) const {
    for (std::shared_ptr<Class> pSuperInterface : m_interfaces) {
        if (pSuperInterface == pIface || pSuperInterface->isSubInterfaceOf(pIface)) {
            return true;
        }
    }
    return false;
}

} // namespace heap
} // namespace jvm